home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************
- #
- # JPEG.c
- #
- # This segment handles JPEG.
- #
- # Author(s): Michael Marinkovich & Guillermo Ortiz
- # marink@apple.com
- #
- # Modification History:
- #
- # 4/3/96 MWM Initial coding
- #
- # Copyright © 1992-96 Apple Computer, Inc., All Rights Reserved
- #
- #
- # You may incorporate this sample code into your applications without
- # restriction, though the sample code has been provided "AS IS" and the
- # responsibility for its operation is 100% yours. However, what you are
- # not permitted to do is to redistribute the source as "DSC Sample Code"
- # after having made changes. If you're going to re-distribute the source,
- # we require that you make it clear in the source that the code was
- # descended from Apple Sample Code, but that you've made changes.
- #
- *************************************************************************************/
-
- #include <Events.h>
- #include <ToolUtils.h>
- #include <Gestalt.h>
- #include <OSUtils.h>
- #include <Palettes.h>
-
- #include "App.h"
- #include "Proto.h"
-
-
- // data unload buffer size
- #define kBufferSize codecMinimumDataSize
-
-
- //----------------------------------------------------------------------
- //
- // ReadJPEG - open a JPEG file with supplied FSSpec.
- //
- //
- //----------------------------------------------------------------------
-
- OSErr ReadJPEG(FSSpec spec, CGrafPtr *newWorld)
- {
- OSErr err = noErr;
- ImageDescriptionHandle desc;
- GWorldPtr theWorld = nil;
- GWorldPtr oldWorld;
- GDHandle oldGD;
- PixMapHandle thePix;
- ICMDataProcRecord loadProc;
- Handle image;
- Rect bounds;
- DLDataRec dataRec;
- long fileLength;
- long dataSize;
- short refNum;
- Ptr stripImage;
-
- GetGWorld(&oldWorld, &oldGD);
-
- if (newWorld != nil)
- {
- SetCursor(*GetCursor(watchCursor)); // set the cursor to a watch while busy
-
- err = FSpOpenDF(&spec, fsRdWrShPerm, &refNum);
- if ( err == noErr )
- {
- desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
- if (desc != nil)
- {
- HLock((Handle)desc);
- err = ReadJPEGHeader(refNum, desc, &bounds);
- if (err == noErr)
- {
- image = NewHandleClear(kBufferSize);
- err = MemError();
- if (image != nil && err == noErr)
- {
- err = NewJPEGWorld(&theWorld, (*desc)->depth, bounds);
- if (theWorld != nil && err == noErr)
- {
- err = SetFPos(refNum, fsFromStart , 0);
- if (err == noErr)
- {
- thePix = GetGWorldPixMap(theWorld);
- LockPixels(thePix);
- SetGWorld(theWorld, nil);
-
- HLock(image);
-
- dataSize = kBufferSize;
-
- // make sure the filesize is greater than the buffersize
- (void)GetEOF(refNum, &fileLength);
- if (kBufferSize > fileLength)
- dataSize = fileLength;
-
- err = FSRead(refNum, &dataSize, *image);
- if (err == noErr)
- {
- fileLength = (*desc)->dataSize - kBufferSize;
- if (fileLength < 0)
- fileLength = 0;
-
- dataRec.refNum = refNum;
- dataRec.fileLength = fileLength;
- dataRec.origPtr = *image;
- dataRec.endPtr = *image + kBufferSize;
- loadProc.dataProc = NewICMDataProc(DataLoadingProc);
- loadProc.dataRefCon = (long)&dataRec;
-
- stripImage = StripAddress(*image);
-
- err = FDecompressImage(stripImage, desc, thePix, &bounds, nil, srcCopy, nil, nil, nil,
- codecHighQuality, anyCodec, kBufferSize, &loadProc, nil);
-
- DisposeRoutineDescriptor(loadProc.dataProc);
-
- HUnlock(image);
-
- UnlockPixels(thePix);
- SetGWorld(oldWorld, oldGD);
-
- if (err == noErr)
- *newWorld = theWorld;
- }
-
- }
- if (err != noErr)
- DisposeGWorld(theWorld);
-
- }
- DisposeHandle(image);
-
- }
-
- }
- HUnlock((Handle)desc);
- DisposeHandle((Handle)desc);
-
- }
- FSClose(refNum); // close the file
-
- }
- SetCursor(&qd.arrow ); // set cursor back to arrow
- }
- else
- err = paramErr;
-
-
- return err;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // ReadJPEGHeader - fill out the ImageDescription with needed info.
- //
- //
- //----------------------------------------------------------------------
-
- OSErr ReadJPEGHeader(short refNum, ImageDescriptionHandle desc, Rect *bounds)
- {
- OSErr err = noErr;
- long imageSize;
- short w = 0, h = 0;
- short skip;
- UInt8 marker;
- Boolean isJFIF = false;
- Boolean readingExtension = false;
-
- // set file position to beginning of file
- err = SetFPos(refNum, fsFromStart , 0);
- if (err != noErr)
- return err;
-
- // get file length so we don't overflow
- err = GetEOF(refNum, &imageSize);
- if (err != noErr)
- return err;
-
- (*desc)->dataSize = imageSize;
-
- while (true) // loop forever
- {
- marker = FindNextMarker(refNum);
-
- switch (marker)
- {
- case kSOIMarker:
- isJFIF = true;
- break;
-
- case kAPPOMarker + 0:
- case kAPPOMarker + 1:
- case kAPPOMarker + 2:
- case kAPPOMarker + 3:
- case kAPPOMarker + 4:
- case kAPPOMarker + 5:
- case kAPPOMarker + 6:
- case kAPPOMarker + 7:
- case kAPPOMarker + 8:
- case kAPPOMarker + 9:
- case kAPPOMarker + 10:
- case kAPPOMarker + 11:
- case kAPPOMarker + 12:
- case kAPPOMarker + 13:
- case kAPPOMarker + 14:
- case kAPPOMarker + 15:
- err = HandleAPPOMarker(marker, refNum, desc, &readingExtension);
- if (err != noErr)
- return err;
- break;
-
- case kCommentMarker:
- SkipLength(refNum);
- break;
-
- case kSOFMarker + 0: // start of frame header marker
- case kSOFMarker + 1:
- case kSOFMarker + 2:
- case kSOFMarker + 3:
- case kSOFMarker + 5:
- case kSOFMarker + 6:
- case kSOFMarker + 7:
- case kSOFMarker + 9:
- case kSOFMarker + 10:
- case kSOFMarker + 11:
- case kSOFMarker + 13:
- case kSOFMarker + 14:
- case kSOFMarker + 15:
- err = HandleSOFMarker(refNum, desc, readingExtension);
- if (err != noErr)
- return err;
-
- if (!readingExtension)
- {
- SetRect(bounds, 0, 0, (*desc)->width, (*desc)->height);
-
- return noErr;
- }
- break;
-
- case kDACMarker:
- skip = ReadWord(refNum) - 2;
- skip *= ReadWord(refNum);
- err = SetFPos(refNum, fsFromMark, skip);
- break;
-
- case kSOSMarker:
- HandleSOSMarker(refNum);
- break;
-
- case kDHTMarker:
- case kDQTMarker:
- case kRSTOMarker:
- SkipLength(refNum);
- break;
-
- case kEOIMarker: // we reached the end of image
- // we are reading an extension so keep going
- if (readingExtension == true)
- readingExtension = false;
-
- break;
- }
-
- }
-
- return err;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // FindNextMarker -
- //
- //
- //----------------------------------------------------------------------
-
- UInt8 FindNextMarker(long refNum)
- {
- UInt8 marker;
-
- marker = ReadByte(refNum);
-
- while (marker == kStartMarker)
- marker = ReadByte(refNum);
-
- while (marker == 0x00)
- {
- marker = ReadByte(refNum);
-
- while (marker != kStartMarker)
- marker = ReadByte(refNum);
-
- marker = ReadByte(refNum);
- }
-
- return marker;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // HandleAPPOMarker -
- //
- //
- //----------------------------------------------------------------------
-
- OSErr HandleAPPOMarker(UInt8 marker, long refNum, ImageDescriptionHandle desc, Boolean *readingExtension)
- {
- OSErr err = noErr;
- Fixed xRes,yRes;
- long length;
- short w = 0, h = 0;
- short units;
- short version;
- UInt8 extension;
- UInt8 JIFF[5];
-
-
- // read skip bytes - header length - skip count
- length = ReadWord(refNum) - 2;
-
- if (marker == kAPPOMarker && length >= 14)
- {
- JIFF[0] = ReadByte(refNum);
- JIFF[1] = ReadByte(refNum);
- JIFF[2] = ReadByte(refNum);
- JIFF[3] = ReadByte(refNum);
- JIFF[4] = ReadByte(refNum);
-
- // check if we really have the JFIF header
- if ( JIFF[0] == 'J' && JIFF[1] == 'F' && JIFF[2] == 'I' && JIFF[3] == 'F' )
- {
- version = ReadWord(refNum);
-
- if ( version < 0x100 )
- {
- err = paramErr;
- return err; // don't know this
- }
- else
- (*desc)->version = version;
-
- units = ReadByte(refNum);
- xRes = ReadWord(refNum);
- yRes = ReadWord(refNum);
-
- switch ( units )
- {
- case 0: // no res, just aspect ratio
- xRes = FixMul(72L << 16, xRes << 16);
- yRes = FixMul(72L << 16, yRes << 16);
- break;
-
- case 1: // dots per inch
- xRes = xRes << 16;
- yRes = yRes << 16;
- break;
-
- case 2: // dots per centimeter (we convert to dpi )
- xRes = FixMul(0x28a3d, xRes << 16);
- yRes = FixMul(0x28a3d, xRes << 16);
- break;
-
- default:
- break;
- }
-
- (*desc)->hRes = xRes;
- (*desc)->vRes = yRes;
-
- length -= 12;
- err = SetFPos(refNum, fsFromMark, length);
-
- }
- else
- {
- if ( JIFF[0] == 'J' && JIFF[1] == 'F' && JIFF[2] == 'X' && JIFF[3] == 'X' )
- {
- *readingExtension = true; // next markers are extensions (ignore)
-
- extension = ReadByte(refNum);
-
- switch (extension)
- {
- case 0x10:
- case 0x11:
- case 0x13:
- break;
-
- default:
- err = paramErr;
- return err;
- }
-
- }
-
- }
-
- }
- else
- err = SetFPos(refNum, fsFromMark, length);
-
- return err;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // HandleSOFMarker -
- //
- //
- //----------------------------------------------------------------------
-
- OSErr HandleSOFMarker(long refNum, ImageDescriptionHandle desc, Boolean readingExtension)
- {
- OSErr err = noErr;
- short w = 0;
- short h = 0;
- short components;
- short length;
-
- if (readingExtension == false)
- {
- length = ReadWord(refNum);
- ReadByte(refNum);
- h = ReadWord(refNum);
- w = ReadWord(refNum);
-
- if (w && h) // make sure we do have something to display
- {
- /* now make up the image description */
- (*desc)->idSize = sizeof(ImageDescription);
- (*desc)->cType = 'jpeg';
- (*desc)->dataRefIndex = 0;
- (*desc)->revisionLevel = 0;
- (*desc)->vendor = 0;
- (*desc)->temporalQuality = 0;
- (*desc)->spatialQuality = codecNormalQuality;
- (*desc)->width = w;
- (*desc)->height = h;
- (*desc)->frameCount = 1;
- BlockMove("\pPhoto - JPEG",(*desc)->name,13);
- (*desc)->clutID = -1;
-
- components = ReadByte(refNum);
-
- switch (components)
- {
- case 1:
- (*desc)->depth = 40;
- break;
-
- case 3:
- (*desc)->depth = 32;
- break;
-
- case 4:
- (*desc)->depth = 32;
- break;
-
- default:
- err = paramErr;
- return err;
- break;
- }
-
- err = SetFPos(refNum, fsFromMark, length - 8);
- return noErr;
-
- }
-
- }
- else
- {
- length = ReadWord(refNum) - 2;
- err = SetFPos(refNum, fsFromMark, length);
- if (err != noErr)
- return err;
- }
-
- return err;
- }
-
- //----------------------------------------------------------------------
- //
- // HandleSOSMarker -
- //
- //
- //----------------------------------------------------------------------
-
- void HandleSOSMarker(long refNum)
- {
- short components;
- short c;
-
- ReadWord(refNum);
- components = ReadByte(refNum);
-
- for (c = 0; c < components; c++)
- ReadWord(refNum);
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // ReadByte -
- //
- //
- //----------------------------------------------------------------------
-
- UInt8 ReadByte(short refNum)
- {
- UInt8 data;
- long bytesNeeded = sizeof(char);
-
- (void)FSRead(refNum, &bytesNeeded, &data);
-
- return data;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // ReadWord -
- //
- //
- //----------------------------------------------------------------------
-
- UInt16 ReadWord(short refNum)
- {
- UInt16 data;
- long bytesNeeded = sizeof(UInt16);
-
- (void)FSRead(refNum, &bytesNeeded, &data);
-
- return data;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // SkipLength -
- //
- //
- //----------------------------------------------------------------------
-
- void SkipLength(long refNum)
- {
- UInt16 skip;
-
- skip = (ReadWord(refNum) - 2);
- SetFPos(refNum, fsFromMark, skip);
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // NewJPEGWorld -
- //
- //
- //----------------------------------------------------------------------
-
- OSErr NewJPEGWorld(GWorldPtr *theWorld, short depth, Rect theRect)
- {
- OSErr err = noErr;
- GWorldPtr oldPort;
- PixMapHandle thePix;
- CTabHandle cTab = nil;
- GDHandle oldGD;
-
- if (theWorld != nil)
- {
- GetGWorld(&oldPort,&oldGD);
-
- // if depth is greater than 32 then the
- // image is grayscale
- if (depth > 32)
- {
- cTab = GetCTable(depth);
- depth = depth - 32;
- }
-
- err = NewGWorld(theWorld, depth, &theRect, cTab, nil, 0L); // try our heap
-
- if (err != noErr)
- err = NewGWorld(theWorld, depth, &theRect, cTab, nil, useTempMem); // else try temp
-
- if (err == noErr && theWorld != nil)
- {
- thePix = GetGWorldPixMap(*theWorld);
-
- if (LockPixels(thePix))
- {
- SetGWorld(*theWorld, nil);
- EraseRect(&theRect);
-
- UnlockPixels(thePix);
- }
- }
-
- SetGWorld(oldPort, oldGD);
- }
- else
- err = paramErr;
-
- return err;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // DoSaveJPEG -
- //
- //
- //----------------------------------------------------------------------
-
- OSErr SaveJPEG(WindowRef window)
- {
- OSErr err = noErr;
- StandardFileReply reply;
- ImageDescriptionHandle desc;
- Handle data;
- Rect srcRect;
- GWorldPtr theWorld;
- PixMapHandle thePix;
- CTabHandle cTab = nil;
- ICMFlushProcRecord flushProc;
- short refNum;
- short depth;
- Str255 title;
- DocHnd doc;
-
-
- GetWTitle(window, title);
- StandardPutFile("\pSave document as:", title, &reply) ;
-
- doc = (DocHnd)GetWRefCon(window);
-
- if (reply.sfGood && doc != nil)
- {
- desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
-
- theWorld = (**doc).world;
-
- if (theWorld != nil && desc != nil)
- {
- srcRect = theWorld->portRect;
- thePix = GetGWorldPixMap(theWorld);
-
- if (LockPixels(thePix))
- {
- // if less than 16-bit then get the color table
- // of our GWorld
- depth = (**thePix).pixelSize;
- if (depth < 16)
- cTab = (**thePix).pmTable;
-
- data = NewHandle(kBufferSize);
- err = MemError();
-
- if (data != nil && err == noErr)
- {
- HLock(data);
-
- if (reply.sfReplacing )
- err = FSpDelete(&reply.sfFile);
-
- err = FSpCreate(&reply.sfFile, 'JVWR', 'JPEG', reply.sfScript);
-
- if (err == noErr)
- err = FSpOpenDF(&reply.sfFile,fsRdWrPerm, &refNum);
-
- if (err == noErr)
- err = SetFPos(refNum, fsFromStart , 0);
-
- if (err == noErr)
- {
- flushProc.flushProc = NewICMFlushProc(DataUnloadProc);
- flushProc.flushRefCon = refNum;
-
- err = FCompressImage(thePix, &srcRect, depth,
- codecNormalQuality, 'jpeg',
- bestCompressionCodec, cTab,
- codecFlagWasCompressed, kBufferSize,
- &flushProc, nil, desc, *data);
- }
-
- if (err == noErr)
- err = SetFPos(refNum, fsFromStart, (**desc).dataSize);
-
- if (err == noErr)
- err = SetEOF(refNum, (**desc).dataSize);
-
- if (err == noErr)
- err = FSClose( refNum );
-
- HUnlock(data);
- DisposeHandle(data);
-
- DisposeRoutineDescriptor(flushProc.flushProc);
- }
-
- }
-
- UnlockPixels(thePix);
-
- }
-
- if (nil != desc)
- DisposeHandle((Handle)desc);
-
- }
- else
- err = memFullErr;
-
- return err;
-
- }
-
-
-
- //----------------------------------------------------------------------
- //
- // DataUnloadProc -
- //
- //
- //----------------------------------------------------------------------
-
- pascal OSErr DataUnloadProc(Ptr data, long bytesNeeded, long refCon)
- {
- OSErr err = noErr;
-
- if (data == nil)
- {
- // if data is nil QuickTime requests a new position in the
- // file from the current mark, offset by bytesNeeded.
- err = SetFPos(refCon, fsFromMark, bytesNeeded);
- }
- else
- {
- err = FSWrite(refCon, &bytesNeeded, data);
- }
-
- return err;
-
- }
-
-
- //----------------------------------------------------------------------
- //
- // DataLoadingProc -
- //
- //
- //----------------------------------------------------------------------
-
- pascal OSErr DataLoadingProc(Ptr *data, long bytesNeeded, long refCon)
- {
- OSErr err = noErr;
- long unusedDataLen;
- long newDataLen;
- DLDataPtr dataRec;
- Ptr newDataPtr;
-
- dataRec = (DLDataPtr)refCon;
-
- if (data == nil)
- {
- err = SetFPos(dataRec->refNum, fsFromMark, bytesNeeded);
- }
- else
- {
- newDataPtr = *data;
-
- // if QT requests more data than is in the buffer
- // we will have to load more.
- if ((newDataPtr + bytesNeeded) >= dataRec->endPtr)
- {
- // move whats left up to the front of the buffer
- unusedDataLen = dataRec->endPtr - newDataPtr;
- BlockMove(newDataPtr,dataRec->origPtr, unusedDataLen);
-
- // now fill the buffer with new data...after the
- // data we moved to the front of the buffer of course.
- newDataLen = kBufferSize - unusedDataLen;
-
- if (newDataLen > dataRec->fileLength)
- newDataLen = dataRec->fileLength;
-
- newDataPtr = dataRec->origPtr + unusedDataLen;
-
- err = FSRead(dataRec->refNum, &newDataLen, newDataPtr);
-
- dataRec->fileLength -= newDataLen;
-
- *data = dataRec->origPtr;
- }
- }
-
-
- return err;
-
- }
-